Skip to content

试写h5手势 1 ​

本节目标:理解“试写h5手势 1”的核心思路,并能把它用于实际开发或面试表达。

import React, { Component } from 'react';
import styles from './index.less';
//

两点的距离

function getDistance(p1, p2) {
    let x = p2.pageX - p1.pageX,
        y = p2.pageY - p1.pageY;
    return Math.sqrt(x * x + y * y);
}
//

两点的夹角

function getAngle(p1, p2) {
    let x = p1.pageX - p2.pageX,
        y = p1.pageY - p2.pageY;
    return (Math.atan2(y, x) * 180) / Math.PI;
}
//

获取中点

function getMidPoint(p1, p2) {
    let x = (p1.pageX + p2.pageX) / 2,
        y = (p1.pageY + p2.pageY) / 2;
    return [x, y];
}
export default class index extends Component {
    state = {
        startX: 0,
        startY: 0,
        left: 0,
        scaleStyle: [1, 0, 0, 1, 0, 0],//

缩放样式

        originPoint: [0, 0],//

缩放原点

        imgNum: 0,//

图片数量

        currImgIndex: 0,
        currImgWidth: 0,
        visible: false,
    }
    componentDidMount() {
        const { imgs } = this.props;
        // console.log('imgs', imgs);
        if (imgs.length > 0) {
            this.setState({ imgNum: imgs.length });
        }
    }
    componentDidUpdate() {
        const { imgs } = this.props;
        const { currImgIndex, currImgWidth: oldCurrImgWidth } = this.state;
        if (imgs.length > 0 && this.swiperNode) {
            this.createEvent();
            const currImgWidth = this.swiperNode.children[`${currImgIndex}`].clientWidth;
            // console.log('ref', currImgWidth);
            if (oldCurrImgWidth !== currImgWidth) {
                this.setState({ currImgWidth })
            }
        }
    }
    createEvent = () => {
        let isTouch = false; //

单指

        let isDoubleTouch = false; //

双指或多指

        let isMove = false;
        let start = [];//

手势触点集合

        let timer = null;
        let now, delta;
        let startPosition, movePosition, endPosition;
        const _this = this;
        let left = 0;
        //

监听基本事件

        this.swiperNode.addEventListener('touchstart', handleTouchStart, false);
        this.swiperNode.addEventListener('touchmove', handleTouchMove, false);
        this.swiperNode.addEventListener('touchend', handleTouchEnd, false);
        function handleTouchStart(e) {
            // e.preventDefault();
            if (e.touches.length >= 2) {
                //

手势操作,记录触点中心

                isDoubleTouch = true;
                start = e.touches;
                let screenMidPoint = getMidPoint(start[0], start[1]);
                let midPoint = [
                    screenMidPoint[0] - e.target.offsetLeft,
                    screenMidPoint[1] - e.target.offsetTop
                ];
                _this.setState({ originPoint: midPoint });
            } else {
                //

点击或滑动操作,记录开始坐标和开始时间

                isTouch = true;
                startPosition = [e.touches[0].pageX, e.touches[0].pageY];
                now = Date.now();
            }
        }
        function handleTouchMove(e) {
            // clearTimeout(timer);
            // e.preventDefault();
            let currImgIndex = parseInt(e.target.id) || 0;
            const { imgNum, left: rawLeft, currImgWidth } = _this.state;
            if (e.touches.length >= 2 && isDoubleTouch) {
                // timer = setTimeout(() => {
                    let scale = parseFloat(getDistance(e.touches[0], e.touches[1]) / getDistance(start[0], start[1]));
                    let tMatrix = [1, 0, 0, 1, 0, 0];
                    tMatrix[0] = tMatrix[0] * scale;
                    tMatrix[3] = tMatrix[3] * scale;
                    console.log('tMatrix:', tMatrix);
                    _this.setState({ scaleStyle: tMatrix.join(',') });
                // }, 17);
            } else if (isTouch) {
                //

滑动,记录最终位置并计算时间间隔

                timer = setTimeout(() => {
                    delta = Date.now() - now;
                    movePosition = [e.touches[0].pageX, e.touches[0].pageY];
                    const hd = Math.abs(startPosition[0] - movePosition[0]);
                    const vd = Math.abs(startPosition[1] - movePosition[1]);
                    //

快速滑动并且滑动达到一定距离才有效

                    if ((delta > 0 && delta < 350) && (hd > 10 || vd > 10)) {
                        clearTimeout(timer);
                        console.log('object');
                        //

判断滑动方向

                        const swiperDirection = hd > vd
                            ? (startPosition[0] - movePosition[0] > 0 ? 'left' : 'right')
                            : (startPosition[1] - movePosition[1] > 0 ? 'top' : 'bottom')
                        //

保存最终位置

                        endPosition = movePosition;
                        if (swiperDirection === 'left') {
                            //

最后一张左拉的情况

                            if (currImgIndex * 1 === imgNum - 1) {
                                left = rawLeft;
                                return;
                            }
                            left = rawLeft - currImgWidth;
                            currImgIndex++;
                        }
                        if (swiperDirection === 'right') {
                            //

第一张右拉的情况

                            if (rawLeft === 0) {
                                left = 0;
                                return;
                            }
                            left = rawLeft + currImgWidth;
                            currImgIndex--;
                        }
                        _this.setState({ left, currImgIndex });
                    }
                }, 100);
            };
        }
        // this.swiperNode.addEventListener('touchend', handleTouchEnd, false);
        // this.swiperNode.addEventListener('touchcancel', handleTouchCancel, false);
        function handleTouchEnd(e) {
            if (isDoubleTouch) {
                isDoubleTouch = false;
            }
        }
        function handleTouchCancel(e) { }
    }
    handleVisible = () => {
        this.setState(state => ({
            visible: !state.visible
        }))
    }
    render() {
        const { imgs, width = '100%', height } = this.props;
        const { left, originPoint, scaleStyle, currImgIndex, visible } = this.state;
        return (
            <div className={styles.imgsViewer} onClick={this.handleVisible}>
                {!visible && imgs.length > 0 && <div className={styles.coverWrap} >
                    <img
                        style={{ width, height }}
                        src={`${imgs[0]}?x-image-process=image/resize,m_fill,h_550`}
                    />
                    <div className={styles.count}>{`1/${imgs.length}`}</div>
                </div>}
                {/*

有图片数据才显示

 */}
                {visible && imgs.length > 0 &&
                    <div className={styles.swiperWrap}>
                        <div
                            style={{
                                position: 'absolute',
                                top: '50%',
                                left: `${left}px`,
                                transform: 'translateY(-50%)',
                                display: 'flex',
                                transition: 'all 0.5s ease-out 0s',
                            }}
                            ref={node => this.swiperNode = node}
                        >
                            {imgs.map((imgUrl, index) => {
                                return (
                                    <div
                                        key={index}
                                        className={styles.imgWrap}
                                    >
                                        <img
                                            id={index}
                                            src={imgUrl}
                                            style={{
                                                transformOrigin: `${originPoint[0]}px ${originPoint[1]}px`,
                                                transform: `matrix(${scaleStyle})`,
                                            }}
                                        />
                                    </div>
                                )
                            })}
                        </div>
                        {/*

指示器

 */}
                        <div
                            style={{
                                position: 'fixed',
                                bottom: '20px',
                                left: '50vw',
                                transform: 'translate(-50%)',
                                padding: 5,
                                zIndex: 99,
                            }}
                        >
                            {imgs.map((_, index) => (
                                <div
                                    key={index}
                                    style={{
                                        display: 'inline-block',
                                        width: 10,
                                        height: 10,
                                        background: '#ddd',
                                        borderRadius: '50%',
                                        marginLeft: 10,
                                        boxShadow: currImgIndex === index ? '0 0 4px 2px #fff' : ''
                                    }}></div>
                            ))}
                        </div>
                    </div>}
            </div >
        )
    }
}

本站总访问 — 次